1 module tools.copyresources;
2 import commons;
3 import std.string;
4 import std.conv:to;
5 import std.json;
6 import std.file;
7 import std.path;
8 
9 
10 struct Cache
11 {
12     DirEntry entry;
13     bool valid;
14 }
15 struct AssetCache
16 {
17     string timeModified;
18     string timeCopied;
19 }
20 
21 string getCachePath(string workingDir, string from, string toWhere)
22 {
23     string temp = buildNormalizedPath(workingDir, from, toWhere);
24     temp = to!string(hashOf(temp));
25     return ".cache/"~temp~".json";
26 }
27 bool cacheExists(string workingDir, string from, string toWhere)
28 {
29     return exists(getCachePath(workingDir,from,toWhere));
30 }
31 
32 string getCacheName(string fileName)
33 {
34     char[] ret = fileName.dup;
35     for(int i= 0; i < ret.length;i++)
36     {
37         if(fileName[i] == '\\')
38             ret[i] = '/';
39     }
40     return cast(string)ret;
41 }
42 
43 bool isCacheValid = true;
44 
45 AssetCache[string] cache;
46 void readCacheFile(string workingDir, string from, string toWhere)
47 {
48     if(!cacheExists(workingDir, from, toWhere))
49         return;
50     JSONValue json = parseJSON(readText(getCachePath(workingDir, from, toWhere)));
51     
52     foreach (string k, JSONValue v; json.object)
53     {
54         JSONValue arr = v.array;
55         cache[k] = AssetCache(arr[0].str, arr[1].str);
56     }
57 }
58 
59 void writeCacheFile(string workingDir, string from, string toWhere)
60 {
61     string toWrite = "{";
62     foreach(k, v; cache)
63     {
64         toWrite~= "\""~k~"\": [\""~ v.timeModified~"\", \""~v.timeCopied~"\"],";
65     }
66     if(!exists(".cache"))
67         mkdir(".cache");
68 
69     std.file.write(getCachePath(workingDir,from,toWhere), toWrite[0..$-1]~"}");
70 }
71 
72 bool isUpToDate(DirEntry which)
73 {
74     string* time = cast(string*)(which.name in cache);
75     if(time is null)
76         return false;
77     return *time == which.timeLastModified().toSimpleString();
78 }
79 
80 
81 /**
82 *   Receives from and where
83 */
84 void copyResources(ref Terminal t, string inputPath, string outputPath, bool clean)
85 {
86     string workingDir = getcwd();
87     //Populate cache with current content
88     readCacheFile(workingDir, inputPath, outputPath);
89     if(clean)
90     {
91         t.writeln("Cleaning target path: ", outputPath);
92         if(exists(outputPath))
93             rmdirRecurse(outputPath);
94     }
95     if(!exists(outputPath))
96         mkdirRecurse(outputPath);
97 
98     //Copy loop
99     uint copyCount = 0;
100     foreach(DirEntry e; dirEntries(inputPath, SpanMode.depth))
101     {
102         if(e.isDir)
103             continue;
104         Cache c = Cache(e, isUpToDate(e));
105         //Check cache validity
106         //Make path relative to where it is started, so it is possible to create same folder structure
107         string relative = relativePath(c.entry.name, inputPath);
108         
109         //Join target path with the source name
110         string name = buildNormalizedPath(outputPath, relative);
111 
112         bool willCopy = false;
113 
114         auto cacheName = getCacheName(relative);
115         AssetCache* temp = (cacheName in cache);
116         if(temp is null)
117         {
118             cache[cacheName] = AssetCache(c.entry.timeLastModified.toSimpleString, "");
119             temp = (cacheName in cache);
120         }
121         else
122         {
123             willCopy = temp.timeModified != c.entry.timeLastModified.toSimpleString;
124             temp.timeModified = c.entry.timeLastModified.toSimpleString;
125         }
126 
127         //If the target exists, check its time copied, if it is the same on the cache, willCopy = false
128         if(exists(name))
129         {
130             if(!willCopy)
131                 willCopy = (temp.timeCopied == "" || temp.timeCopied != e.timeLastModified.toSimpleString);
132         }
133         //Check if the path where the name is exists, for creating a folder for it
134         else
135         {
136             long ind = lastIndexOf(name, dirSeparator);
137             string namePath = name[0..ind];
138             if(!exists(namePath))
139                 mkdirRecurse(namePath);
140             willCopy = true;
141         }
142         
143         if(willCopy)
144         {
145             copyCount++;
146             t.writeln(c.entry.name, " -> ", name);
147             copy(c.entry.name, name);
148             cache[cacheName].timeCopied = DirEntry(name).timeLastModified.toSimpleString;
149         }
150     }
151     if(copyCount != 0)
152     {
153         writeCacheFile(workingDir, inputPath, outputPath);
154         t.writeln("CopyResources: Copied ", copyCount, " files");
155     }
156     else
157     {
158         t.writeln("CopyResources: Target folder '"~outputPath~"' is up to date");
159     }
160 }